import { isEmpty } from "../isEmpty";
import { equals } from "../equals";
import { valueOrDefault } from "../valueOrDefault";

export enum SubscriberSort {
  FIRST,
  NORMAL,
  LAST,
}

interface IFn {
  (...args: any[]): any;
}

type ISaveRecords = Record<string, Array<[SubscriberSort, IFn, number]>>;

// 保存的记录
const pubSubCache: Map<string, ISaveRecords> = new Map();

/**
 * @author lihh
 * @description 进行订阅
 * @param saveRecords 表示单条保存记录
 * @param type 订阅类型
 * @param fn 订阅方法
 * @param sortValue 要排序的值
 * @param sortNumber 排序字段
 * @returns
 */
const onInner = (
  saveRecords: ISaveRecords,
  type: string,
  fn: IFn,
  sortValue = SubscriberSort.NORMAL,
  sortNumber = 1
) => {
  // 这里判断 第三个参数
  if (
    ![
      SubscriberSort.FIRST,
      SubscriberSort.NORMAL,
      SubscriberSort.LAST,
    ].includes(sortValue)
  )
    throw new Error(
      `params<sortValue> must be a SubscriberSort.FIRST, SubscriberSort.NORMAL, SubscriberSort.LAST`
    );

  // 拿到相关类型 的最后一个值
  const arr = saveRecords[type] || (saveRecords[type] = []);
  // 判断是否为空
  if (isEmpty(arr)) {
    // 第三个参数 参数排序
    arr.push([sortValue, fn, sortNumber]);
    return;
  }

  // 判断相关的值 是否存在
  const index = arr.findIndex(
    (el) => equals(el[0], sortValue) && equals(el[1], fn)
  );
  if (index > -1) return;

  // 添加元素
  arr.push([sortValue, fn, sortNumber]);

  // 先按 时间戳 排序
  arr.sort((a, b) => a[2] - b[2]);
  // 按照 sortValue 排序
  arr.sort((a, b) => a[0] - b[0]);
};

/**
 * @author lihh
 * @description 将订阅的函数进行触发
 * @param saveRecords 单挑保存记录
 * @param type 触发类型
 * @param args 剩余参数
 */
const emitInner = (
  saveRecords: ISaveRecords,
  type: string,
  ...args: unknown[]
) => {
  const fns = saveRecords[type] || (saveRecords[type] = []);
  fns.forEach((fn) => fn[1](...args));
};

/**
 * @author lihh
 * @description 取消订阅
 * @param saveRecords 单挑保存记录
 * @param type 类型
 */
const offInner = (saveRecords: ISaveRecords, type: string) => {
  if (isEmpty(type))
    throw new Error(`off function params【type】 is not empty`);
  const keys: string[] = [type];
  keys.forEach((name: string) => {
    if (saveRecords[name]) Reflect.deleteProperty(saveRecords, name);
  });
};

/**
 * @author lihh
 * @param virtualHost 发布订阅数据隔离，模拟 rabbitmq 的virtualHost的实现方案
 * @return [订阅, 发布, 取消]
 */
export const usePubSub = (virtualHost = "/") => {
  // 表示 on订阅方法
  const on = (
    type: string,
    fn: IFn,
    sortValue = SubscriberSort.NORMAL,
    sortNumber = 1
  ) => {
    onInner(
      valueOrDefault(pubSubCache.get(virtualHost), {})!,
      type,
      fn,
      sortValue,
      sortNumber
    );
  };

  // 表示 emit方法
  const emit = (type: string, ...args: unknown[]) => {
    emitInner(valueOrDefault(pubSubCache.get(virtualHost), {})!, type, ...args);
  };

  // 表示 off方法
  const off = (type: string) => {
    offInner(valueOrDefault(pubSubCache.get(virtualHost), {})!, type);
  };

  return [on, emit, off];
};
